//
// Created by mio on 25-5-19.
//

#include "H264Decoder.h"

#include "VideoConvert.h"

H264Decoder::H264Decoder(AVContext *ac, QObject *parent) : QThread(parent), avContext_(ac)
{
    videoConver_.reset(new VideoConvert());
    yuv_frame_ = AVFramePtr(av_frame_alloc(),[](AVFrame* p){av_frame_free(&p);});
}

H264Decoder::~H264Decoder()
{
    Close();
}

// TODO: 可优化为硬件解码
int H264Decoder::Open(const AVCodecParameters *codecParamer)
{
    if (is_initial_ || !codecParamer) return -1;

    codec_ = (AVCodec*)avcodec_find_decoder(codecParamer->codec_id);
    if (!codec_) return -1;

    codecCtx_ = avcodec_alloc_context3(codec_);
    if (avcodec_parameters_to_context(codecCtx_, codecParamer) < 0) return -1;

    codecCtx_->flags |= AV_CODEC_FLAG2_FAST;

    if (avcodec_open2(codecCtx_, codec_, nullptr) != 0) return -1;

    avContext_->video_width = codecCtx_->width;
    avContext_->video_height = codecCtx_->height;
    avContext_->video_fmt = AV_PIX_FMT_YUV420P;

    if (!videoConver_->Open(codecCtx_->width, codecCtx_->height, codecCtx_->pix_fmt, codecCtx_->width, codecCtx_->height
                            , AV_PIX_FMT_YUV420P))
    {
        return -1;
    }

    is_initial_ = true;
    start();
    return 0;
}

void H264Decoder::Close()
{
    quit_ = true;
    if (isRunning()) {
        this->quit();
        this->wait();
    }
}

void H264Decoder::run()
{
    AVPacketPtr pkt = nullptr;
    while (!quit_ && videoConver_)
    {
        if (!video_queue_.size())
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            continue;
        }
        video_queue_.pop(pkt);
        if (avcodec_send_packet(codecCtx_, pkt.get())) break;
        int ret = 0;
        while (ret >= 0) {
            ret = avcodec_receive_frame(codecCtx_, yuv_frame_.get());
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            AVFramePtr outframe = nullptr;
            if (videoConver_->Convert(yuv_frame_, outframe))
            {
                if (outframe)
                {
                    avContext_->video_queue_.push(outframe);
                }
            }
        }
    }
}
